/**
 * @author W
 * @date 2020/4/6 15:36
 */

// 交叉类型：交叉类型是将多个类型合并为一个类型。
// 这让我们可以把现有的多种类型叠加到一起成为一种类型，它包含了所需的所有类型的特性。
// 例如， Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。 就是说这个类型的对象同时拥有了这三种类型的成员。
// 交叉类型表示一种类型（取所有属性的并集）；而继承则是表示一种值（子类继承父类的属性，也可以任意定义自己的属性）
{
    type Name = 'Blob' & 'Wang';
    const name:Name = <Name>'Blob' // 使用类型断言才能不报错，这里有区别于联合类型，因为交叉类型是多个类型合并为一个类型。
}
{
    interface Dog {
        run(): void;

        name: string
    }

    interface Cat {
        jump(): void;

        age: number
    }

    // 定义pet 为 Dog和Cat的交叉类型，它包含了所有类型的属性或方法。
    let pet: Dog & Cat = {
        run(): void {
            console.log('run...')
        },
        jump(): void {
            console.log('jump...')
        },
        name: 'tuFei',
        age: 10
    }

    pet.run()
}

// 因为交叉类型不能自己添加额外的属性，如果想要使用交叉类型的同时又能实现自己的属性
{
    interface Dog {
        run(): void;

        name: string
    }

    interface Cat {
        jump(): void;
        fly:(type:string)=>void;
        age: number
    }

    // 将 Dog 和 Cat 的交叉类型取个类型别名Animal
    type DogCat = Dog & Cat;

    // 定义一个类，实现这个交叉类型的接口，这样就可以定义自己的属性或方法了
    class Animal implements DogCat {
        constructor(public name:string,public age:number,public grade:number) {}

        fly(type:string){
            console.log(`${this.name} ${type} fly ...`)
        }
        run(){
            console.log(`${this.name} run ...`)
        }
        jump(){
            console.log(`${this.name} jump ...`)
        }
        eat(){
            console.log(`${this.name} eat ...`)
        }
    }

    let animal = new Animal('LiHao',22,100);
    animal.run();
    animal.jump()
    animal.eat()
    animal.fly('bird')
}

// 联合类型：当我们不确定一个值的类型时，可以为其指定多个可能的类型

{
    // a 既可以为number 也可以是string
    let a:number | string;
    a = 1;
    a ='wang'
}

// 在访问一个联合的属性时，由于编译时我们并不知道它具体的类型，所以只能访问所有类型共同有的属性（取交集）
{
    interface Dog {
        run(): void;
        name: string
    }

    interface Cat {
        jump(): void;
        name: string
    }

    // 只能访问其共有的属性，除非使用类型断言或者类型保护，这里就不再重复举例了
    const f1 = (a:Cat|Dog): string => a.name;

    console.log(f1({
        run(): void {
            console.log('run...')
        },
        name:'smallDog'
    }))
}
